package com.allen.app.common.utils;

import cn.hutool.core.util.StrUtil;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

/**
 * 字符串操作工具类
 *
 * @author leon[234239150@qq.com]
 * @version 1.0
 * @see org.springframework.util.StringUtils spring-core.jar -&gt; StringUtils.java
 * @since loose v1.0
 */
public final class StringUtils extends org.springframework.util.StringUtils {
    /**
     * NULL_REFERENCE = null
     */
    public static final String NULL_REFERENCE = null;
    /**
     * NULL_VALUE = ""
     */
    public static final String NULL_VALUE = "";

    /**
     * Represents a failed index search.
     *
     * @since 2.1
     */
    public static final int INDEX_NOT_FOUND = -1;

    public static final char DELIM_START = '{';

    public static final String DELIM_STR = "{}";


    public static final String DEFAULT_OPEN_TOKEN = "$" + DELIM_START;

    public static final String DEFAULT_CLOSE_TOKEN = "}";

    public static final String EMOJI_REGEX = "[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]";

    /**
     * 格式化字符串<br/>
     * e.g: StringUtils.format("http://{}/{}", "www.leon.com", "example") = http://www.leon.com/example
     *
     * @param text 格式化字符串
     * @param args 参数
     * @return 格式化后的字符串
     */
    public static String format(String text, Object... args) {
        return StrUtil.format(text, args);
    }

    /**
     * 替换字符串中的Emoji表情
     *
     * @param text        带Emoji表情的字符串
     * @param replacement 要替换的字符
     * @return 替换Emoji表情的字符串
     */
    public static String replaceEmoji(String text, String replacement) {
        if (StringUtils.isBlank(text)) {
            return NULL_VALUE;
        }
        try {
            Pattern emoji = Pattern.compile(EMOJI_REGEX, Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE);
            Matcher emojiMatcher = emoji.matcher(text);
            if (emojiMatcher.find()) {
                String temp = text.substring(emojiMatcher.start(), emojiMatcher.end());
                text = text.replaceAll(temp, replacement);
            }
            // 去除4字节字符
            byte[] conbyte = text.getBytes();
            for (int i = 0; i < conbyte.length; i++) {
                if ((conbyte[i] & 0xF8) == 0xF0) {
                    for (int j = 0; j < 4; j++) {
                        conbyte[i + j] = 0x30;// 将当前字符变为“0000”
                    }
                    i += 3;
                }
            }
            text = new String(conbyte);
            return text.replaceAll("0000", replacement); // 去除“0000”
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 将字符串中变量替换为指定值<br/>
     * e.g: ${variable}
     *
     * @param text      存在变量的文本，如果文本中不存在变量，则原样输出
     * @param variables 变量集合
     * @return 处理后的字符串
     */
    public static String tokenParser(String text, Map<String, Object> variables) {
        return tokenParser(DEFAULT_OPEN_TOKEN, DEFAULT_CLOSE_TOKEN, text, variables);
    }

    /**
     * 将字符串中变量替换为指定值
     *
     * @param openToken  包裹变量的前置字符串，例如EL表达式中的${
     * @param closeToken 包裹变量的后置字符串，例如EL表达式中的}
     * @param text       存在变量的文本，如果文本中不存在变量，则原样输出
     * @param variables  变量集合
     * @return 处理后的字符串
     */
    public static String tokenParser(String openToken, String closeToken, String text, Map<String, Object> variables) {
        StringBuilder builder = new StringBuilder();
        if (!Objects.equals(text, NULL_REFERENCE) && text.length() > 0) {
            char[] src = text.toCharArray();
            int offset = 0;
            int start = text.indexOf(openToken, offset);
            while (start > -1) {
                if (start > 0 && src[start - 1] == '\\') {
                    builder.append(src, offset, start - 1).append(openToken);
                    offset = start + openToken.length();
                } else {
                    int end = text.indexOf(closeToken, start);
                    if (end == -1) {
                        builder.append(src, offset, src.length - offset);
                        offset = src.length;
                    } else {
                        builder.append(src, offset, start - offset);
                        offset = start + openToken.length();
                        String content = new String(src, offset, end - offset);

                        if (variables != null && variables.containsKey(content)) {
                            builder.append(variables.get(content));
                        } else {
                            builder.append(openToken).append(content).append(closeToken);
                        }
                        offset = end + closeToken.length();
                    }
                }
                start = text.indexOf(openToken, offset);
            }
            if (offset < src.length) {
                builder.append(src, offset, src.length - offset);
            }
        }
        if (builder.indexOf("\\") > -1) {
            return builder.toString().replace("\\", "\\\\");
        }
        return builder.toString();
    }

    /**
     * <p>Checks if a String is whitespace, empty ("") or null.</p>
     * <p>
     * <pre>
     * StringUtils.isBlank(null)      = true
     * StringUtils.isBlank("")        = true
     * StringUtils.isBlank(" ")       = true
     * StringUtils.isBlank("bob")     = false
     * StringUtils.isBlank("  bob  ") = false
     * </pre>
     *
     * @param str the String to check, may be null
     * @return <code>true</code> if the String is null, empty or whitespace
     */
    public static boolean isBlank(String str) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((!Character.isWhitespace(str.charAt(i)))) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>Joins the elements of the provided <code>Collection</code> into
     * a single String containing the provided elements.</p>
     * <p>
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").</p>
     *
     * @param separator  the separator character to use, null treated as ""
     * @param collection the <code>Collection</code> of values to join together, may be null
     * @return the joined String, <code>null</code> if null iterator input
     */
    public static String join(String separator, Object... collection) {
        if (ObjectUtils.isEmpty(collection)) {
            return NULL_REFERENCE;
        } else {
            int startIndex = 0, endIndex = collection.length;
            int noOfItems = endIndex - startIndex;
            if (noOfItems <= 0) {
                return NULL_VALUE;
            } else {
                StringBuilder buf = new StringBuilder(noOfItems * 16);
                for (int i = startIndex; i < endIndex; ++i) {
                    if (i > startIndex) {
                        buf.append(separator);
                    }
                    if (Objects.nonNull(collection[i])) {
                        buf.append(collection[i].toString());
                    }
                }
                return buf.toString();
            }
        }
    }


    /**
     * <p>Checks if a String is not whitespace, empty ("") or null.</p>
     * <p>
     * <pre>
     * StringUtils.isBlank(null)      = false
     * StringUtils.isBlank("")        = false
     * StringUtils.isBlank(" ")       = false
     * StringUtils.isBlank("bob")     = true
     * StringUtils.isBlank("  bob  ") = true
     * </pre>
     *
     * @param str the String to check, may be null
     * @return <code>true</code> if the String is not null, empty or whitespace
     */
    public static boolean isNotBlank(String str) {
        return !isBlank(str);
    }

    /**
     * Check whether the given {@code String} is not empty.
     * <p>This method accepts any Object as an argument, comparing it to
     * {@code null} and the not empty String. As a consequence, this method
     * will never return {@code true} for a non-null non-String object.
     * <p>The Object signature is useful for general attribute handling code
     * that commonly deals with Strings but generally has to iterate over
     * Objects since attributes may e.g. be primitive value objects as well.
     *
     * @param str the candidate String
     * @since 3.2.1
     */
    public static boolean isNotEmpty(Object str) {
        return !isEmpty(str);
    }

    /**
     * <p>Strips any of a set of characters from the end of a String.</p>
     * <p>
     * <p>A {@code null} input String returns {@code null}.
     * An empty string ("") input returns the empty string.</p>
     * <p>
     * <p>If the stripChars String is {@code null}, whitespace is
     * stripped as defined by {@link Character#isWhitespace(char)}.</p>
     * <p>
     * <pre>
     * StringUtils.stripEnd(null, *)          = null
     * StringUtils.stripEnd("", *)            = ""
     * StringUtils.stripEnd("abc", "")        = "abc"
     * StringUtils.stripEnd("abc", null)      = "abc"
     * StringUtils.stripEnd("  abc", null)    = "  abc"
     * StringUtils.stripEnd("abc  ", null)    = "abc"
     * StringUtils.stripEnd(" abc ", null)    = " abc"
     * StringUtils.stripEnd("  abcyx", "xyz") = "  abc"
     * StringUtils.stripEnd("120.00", ".0")   = "12"
     * </pre>
     *
     * @param str        the String to remove characters from, may be null
     * @param stripChars the set of characters to remove, null treated as whitespace
     * @return the stripped String, {@code null} if null String input
     */
    public static String stripEnd(final String str, final String stripChars) {
        int end;
        if (Objects.isNull(str) || (end = str.length()) == 0) {
            return str;
        }

        if (Objects.isNull(stripChars)) {
            while (end != 0 && Character.isWhitespace(str.charAt(end - 1))) {
                end--;
            }
        } else if (stripChars.isEmpty()) {
            return str;
        } else {
            while (end != 0 && stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND) {
                end--;
            }
        }
        return str.substring(0, end);
    }

    public static void assertHasLength(String... args) {
        for (String arg : args) {
            Assert
                .hasLength(arg, format("[Assertion failed] - '{}' must have length; it must not be null or empty.", arg));
        }
    }

    /**
    * 拼接字符串.
    * @param split  分隔符
    * @param str  可变字符串数组
    * @return java.lang.String
    * @author Limiaojun
    * @date 2020/2/10 10:42
    */
    public static String append(String split,String... str){
        if(Objects.isNull(str) || str.length ==0){
            return "";
        }
        StringBuilder sbu = new StringBuilder("");
        for (String s : str) {
            sbu.append(s).append(split);
        }
        if(CheckObjectUtils.nonNull(split)){
            return sbu.substring(0,sbu.length()-1);
        }
        return sbu.toString();
    }

    /**
     * 处理string 如果是空字符串和undefined, 返回默认值.
     * @param str  str
     * @param defaultStr  默认值
     * @return boolean
     * @author Limiaojun
     * @date 2020/3/29 0029 11:54
     */
    public static String handelBlankStr(String str,String defaultStr){
        if(StringUtils.isBlank(str)){
            return defaultStr;
        }
        if("undefined".equals(str)){
            return defaultStr;
        }
        return str;
    }

    /**
     *
     * Description: 移除最后一个字符
     *
     * @author Limiaojun
     * @date 2019/12/24 19:45
     **/
    public static String removeLastChar(String str) {
        if (CheckObjectUtils.isNull(str)) {
            return "";
        }
        return str.substring(0, str.length() == 0 ? 0 : str.length() - 1);
    }
}
